package com.baturu.vin.benz.service.impl.util;

import com.baturu.vin.benz.dto.BenzEPCPartsInfoDTO;
import com.baturu.vin.benz.dto.BenzPartCatInfoDTO;
import com.baturu.vin.benz.dto.footnote.Footnote;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.apache.commons.lang3.StringUtils;

import java.util.*;
import java.util.stream.Stream;

/**
 * 奔驰vin码和配件编码解析器
 * sa代码过滤(CodeB)
 * @author 蒋鑫磊
 * @Time 2016/11/15.
 */
public class BenzValidateCodeBUtil {

    public static void scorecodeBString(Set<String> saCodeSet, Footnote fn){
        String codeB = fn.getCodeBs();
        float score;
        if (codeB == null || codeB.trim().equals("")) {
            score = 0.5f;
        }
        else {
            score = scoreCodeBStirng(saCodeSet, codeB);
        }
        fn.setCodeBScore(score);
    }

    private static int scoreCodeBStirng(Set<String> saCodeSet, String codeB) {
        if (codeB == null || codeB.trim().equals("")) {
            return 0;
        }
        OptionalInt max = Stream.of(StringUtils.split(codeB, "/"))
                .filter(curCodeB -> validateCodeB(saCodeSet, curCodeB, "D"))
                .mapToInt(curCodeB -> StringUtils.split(curCodeB, "+-").length - countStringOccurances(curCodeB, "-"))
                .max();
        return max.orElse(0);
    }

    public static int countStringOccurances(String strToSearch, String strToFind) {
        int count = 0;
        int index;
        while ((index = strToSearch.indexOf(strToFind)) != -1) {
            ++count;
            strToSearch = strToSearch.substring(index + 1);
        }
        return count;
    }

    public static boolean validateCodeB(Set<String> saCodeSet, String codeB, String source) {
        String[][] newTable = {{"^-1", "0"}, {"^-0", "1"}, {"/-1", "/0"}, {"/-0", "/1"}, {"1+1", "1"}, {"1+0", "0"}, {"0+1", "0"}, {"0+0", "0"}, {"1-1", "0"}, {"1-0", "1"}, {"0-1", "0"}, {"0-0", "0"}, {"1/1", "1"}, {"1/0", "1"}, {"0/1", "1"}, {"0/0", "0"}};
        String[][] oldTable = {{"/1+", null}, {"/0+", null}, {"/1-", null}, {"/0-", null}, {"+1-", null}, {"+0-", null}, {"+1/", null}, {"+0/", null}, {"-1+", null}, {"-0+", null}, {"-1/", null}, {"-0/", null}, {"/+", null}, {"/-", null}, {"-+", null}, {"+/", null}, {"-/", null}, {"+-", "-"}, {"^-1", "0"}, {"^-0", "1"}, {"(-1", "(0"}, {"(-0", "(1"}, {"(1)", "1"}, {"(0)", "0"}, {"1+1", "1"}, {"1+0", "0"}, {"0+1", "0"}, {"0+0", "0"}, {"1-1", "0"}, {"1-0", "1"}, {"0-1", "0"}, {"0-0", "0"}, {"1/1", "1"}, {"1/0", "1"}, {"0/1", "1"}, {"0/0", "0"}};
        StringBuffer codeBSB;
        String[][] useTable;
        if (source == null || (!source.equals("D") && !source.equals("M"))) {
            useTable = oldTable;
        } else {
            useTable = newTable;
        }
        if (codeB == null || codeB.trim().equals("")) {
            return true;
        }
        codeBSB = new StringBuffer(codeB);
        for (int i = 0; i < codeBSB.length() - 1; ++i) {
            if (Character.isLetterOrDigit(codeBSB.charAt(i))) {
                int codeSize = 1;
                if (i + 1 < codeBSB.length() && Character.isLetterOrDigit(codeBSB.charAt(i + 1))) {
                    codeSize = 2;
                    if (i + 2 < codeBSB.length() && Character.isLetterOrDigit(codeBSB.charAt(i + 2))) {
                        codeSize = 3;
                        if (i + 3 < codeBSB.length() && Character.isLetterOrDigit(codeBSB.charAt(i + 3))) {
                            codeSize = 4;
                        }
                    }
                }
                int currLen = codeBSB.length();
                char[] saCode = new char[codeSize];
                char[] temp = new char[currLen - i - codeSize];
                codeBSB.getChars(i, i + codeSize, saCode, 0);
                codeBSB.getChars(i + codeSize, currLen, temp, 0);
                codeBSB.setLength(i);
                codeBSB.append(saCodeSet.contains(new String(saCode)) ? '1' : '0');
                codeBSB.append(temp);
            }
        }
        codeB = stripSpaces(codeBSB.toString());
        if (source != null && (source.equals("D") || source.equals("M"))) {
            String reducedCodeB = "";
            StringTokenizer st = new StringTokenizer(codeB, "/");
            while (st.hasMoreTokens()) {
                String curCodeB = st.nextToken();
                while (curCodeB.length() != 1) {
                    if (curCodeB.startsWith("-")) {
                        if (curCodeB.charAt(1) == '0') {
                            curCodeB = "1" + curCodeB.substring(2);
                        } else {
                            curCodeB = "0" + curCodeB.substring(2);
                        }
                    } else if (curCodeB.startsWith("+")) {
                        curCodeB = curCodeB.substring(1);
                    } else {
                        if (!Character.isDigit(curCodeB.charAt(0)) || (curCodeB.charAt(1) != '+' && curCodeB.charAt(1) != '-') || !Character.isDigit(curCodeB.charAt(2))) {
                            return true;
                        }
                        if (curCodeB.charAt(1) == '+') {
                            if (curCodeB.startsWith("1+1")) {
                                curCodeB = "1" + curCodeB.substring(3);
                            } else {
                                curCodeB = "0" + curCodeB.substring(3);
                            }
                        } else {
                            if (curCodeB.charAt(1) != '-') {
                                return true;
                            }
                            if (curCodeB.startsWith("1-0")) {
                                curCodeB = "1" + curCodeB.substring(3);
                            } else {
                                curCodeB = "0" + curCodeB.substring(3);
                            }
                        }
                    }
                }
                reducedCodeB += curCodeB;
            }
            if (reducedCodeB.indexOf(49) != -1) {
                codeB = "1";
            } else {
                codeB = "0";
            }
        } else {
            boolean changeMade = true;
            while (changeMade) {
                changeMade = false;
                for (int i = 0; i < useTable.length; ++i) {
                    int len = useTable[i][0].length();
                    int match;
                    if (useTable[i][0].substring(0, 1).equals("^")) {
                        match = codeB.indexOf(useTable[i][0].substring(1));
                        if (match > 0) {
                            match = -1;
                        }
                        --len;
                    } else {
                        match = codeB.indexOf(useTable[i][0]);
                    }
                    if (match >= 0) {
                        if (useTable[i][1] == null) {
                            return false;
                        }
                        codeB = codeB.substring(0, match) + useTable[i][1] + codeB.substring(match + len);
                        changeMade = true;
                        --i;
                    }
                }
            }
        }
        return codeB.length() == 1 && codeB.equals("1");
    }

    private static String stripSpaces(String s) {
        StringBuilder curCodeB = new StringBuilder();
        StringTokenizer st = new StringTokenizer(s, " ");
        while (st.hasMoreTokens()) {
            curCodeB.append(st.nextToken());
        }
        if (!curCodeB.toString().trim().equals("")) {
            return curCodeB.toString();
        }
        return s;
    }

    public static boolean extendCodeFilter(List<BenzEPCPartsInfoDTO> partList, Map<String, BenzPartCatInfoDTO> catInfoMapByCatNum) {
        String catNum = partList.get(0).getCatNum();
        String source = catInfoMapByCatNum.get(catNum).getSource();
        return StringUtils.isNotBlank(source) && (source.equals("D") || source.equals("M"));
    }

    public static Map<String, List<BenzEPCPartsInfoDTO>> getPartsByCallout(List<BenzEPCPartsInfoDTO> epcPartsInfos) {
        Map<String, List<BenzEPCPartsInfoDTO>> partByCalloutHash = Maps.newHashMap();
        for (BenzEPCPartsInfoDTO epcPartsInfo : epcPartsInfos) {
            addPartToMap(partByCalloutHash, epcPartsInfo);
        }
        return partByCalloutHash;
    }

    private static void addPartToMap(Map<String, List<BenzEPCPartsInfoDTO>> partByCalloutHash, BenzEPCPartsInfoDTO epcPartsInfo) {
        String posAddress = epcPartsInfo.getPosAddr();
        String callout = epcPartsInfo.getCallout();
        String hashKey = callout + "_" + posAddress;
        if (StringUtils.isBlank(callout) || callout.equals("-----")) {
            return;
        }
        if (partByCalloutHash.containsKey(hashKey)) {
            List<BenzEPCPartsInfoDTO> partsWithSameCallout = partByCalloutHash.get(hashKey);
            partsWithSameCallout.add(epcPartsInfo);
        } else {
            List<BenzEPCPartsInfoDTO> partsWithSameCallout = Lists.newArrayList();
            partsWithSameCallout.add(epcPartsInfo);
            partByCalloutHash.put(hashKey, partsWithSameCallout);
        }
    }

}
